package com.letu.aopcache;

import com.letu.aopcache.clone.ICloner;
import com.letu.aopcache.handler.CacheHandler;
import com.letu.aopcache.interceptor.AutoloadCacheProperties;
import com.letu.aopcache.interceptor.CacheMethodInterceptor;
import com.letu.aopcache.manager.*;
import com.letu.aopcache.model.AutoLoadConfig;
import com.letu.aopcache.parser.AbstractScriptParser;
import com.letu.aopcache.parser.SpringELParser;
import com.letu.aopcache.serializer.StringSerializer;
import com.letu.cache.lock.DistributedLock;
import com.letu.cache.lock.RedisDistributedLock;
import com.letu.cache.properties.CustomCacheProperties;
import com.letu.cache.properties.SerializerType;
import com.letu.cache.redis.RedisOps;
import com.letu.cache.repository.CacheOps;
import com.letu.cache.repository.CachePlusOps;
import com.letu.cache.repository.CaffeineCacheOps;
import com.letu.cache.repository.impl.CaffeineOpsImpl;
import com.letu.cache.repository.impl.RedisOpsImpl;
import com.letu.cache.utils.ProtoStuffSerializer;
import com.letu.cache.utils.RedisObjectSerializer;
import com.letu.core.utils.StrPool;
import com.google.common.collect.Maps;
import lombok.RequiredArgsConstructor;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.CacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.util.Map;
import java.util.Objects;
import java.util.Optional;


/**
 * redis 配置类
 *
 * @author ligang
 * @date 2019-08-06 10:42
 */
@RequiredArgsConstructor
public abstract class CacheConfigure {
    @Value("${spring.application.name}")
    private String applicationName;

//    static CaffeineCacheOps caffeineCacheOps = new CaffeineOpsImpl();

    @Qualifier("caffeineOpsImpl")
    @Autowired
    private CaffeineCacheOps caffeineCacheOps;


    @Autowired
    private AutoloadCacheProperties config;

    @Autowired
    private DistributedLock distributedLock;

    @Autowired
    private CacheOps cacheOps;
    //拦截目录设置
    public static final String traceExecution = "execution(* com.letu.*.service..*.*(..))||execution(* com.letu.*.handler..*.*(..))||execution(* com.letu.*.manager..*.*(..))";
//    public static final String traceExecution = "execution(* com.letu.*.service..*.*(..)) ";
    //    public static final String traceExecution = "execution(* com.letu.projectcore.service..*.*(..))";

    /**
     * 表达式解析器{@link AbstractScriptParser AbstractScriptParser} 注入规则：<br>
     * 如果导入了Ognl的jar包，优先 使用Ognl表达式：{@link
     *
     * @return
     */
    @Bean
    @ConditionalOnMissingBean(AbstractScriptParser.class)
    public AbstractScriptParser autoloadCacheScriptParser() {
        return new SpringELParser();
    }

    @Bean
    public AutoLoadConfig getAutoLoadConfig() {
        AutoLoadConfig autoLoadConfig = new AutoLoadConfig();
        autoLoadConfig.setNamespace(applicationName);
        return autoLoadConfig;
    }

    @Bean()
    public ICacheManager getCachemanage() {
        AbstractRedisCacheManager abstractRedisCacheManager = new AbstractRedisCacheManager(cacheOps,caffeineCacheOps);
        return abstractRedisCacheManager;
    }


    @Bean()
    @ConditionalOnBean({AutoLoadConfig.class})
    public RelatedCacheManager getRelatedCacheManager(ICacheManager cacheManager, AbstractScriptParser scriptParser, AutoLoadConfig config,
                                                      ICloner cloner, CacheOps cacheOps) {
        RelatedCacheManager relatedCacheManager = new RelatedCacheManager(cacheManager, scriptParser, cacheOps,config,caffeineCacheOps);
        return relatedCacheManager;
    }


    @Bean()
    public MethodMsgManager getMethodMsgManager() {
        return new MethodMsgManager();
    }

    @Bean()
    public TableCacheManager getTableCacheManager(ICacheManager cacheManager, AbstractScriptParser scriptParser, AutoLoadConfig config,
                                                  ICloner cloner, CacheOps cacheOps) {
        return new TableCacheManager(cacheManager, scriptParser, config, cloner, cacheOps,caffeineCacheOps);
    }

    @Bean()
    public ICloner getCloner() {
        StringSerializer stringSerializer = new StringSerializer();
        return stringSerializer;
    }

    @Bean()
    @ConditionalOnMissingBean(CacheHandler.class)
    @ConditionalOnBean({ICacheManager.class, AbstractScriptParser.class, ICloner.class})
    public CacheKeyManager getCacheKeyManager(ICacheManager cacheManager, AbstractScriptParser scriptParser, AutoLoadConfig config) {
        CacheKeyManager cacheKeyManager = new CacheKeyManager(cacheManager, scriptParser, config);
        return cacheKeyManager;
    }

    @Bean()
    @ConditionalOnMissingBean(CacheHandler.class)
    @ConditionalOnBean({ICacheManager.class, AbstractScriptParser.class, ICloner.class})
    public CacheHandler getCacheHandler(ICacheManager cacheManager, AbstractScriptParser scriptParser,
                                        ICloner cloner, RelatedCacheManager relatedCacheManager, CacheKeyManager cacheKeyManager,
                                        MethodMsgManager methodMsgManager, TableCacheManager tableCacheManager) {
        CacheHandler cacheHandler = new CacheHandler(cacheManager, scriptParser, config, cloner, cacheKeyManager, relatedCacheManager, methodMsgManager, tableCacheManager,distributedLock);
        return cacheHandler;
    }


    @Bean
    @ConditionalOnBean(CacheHandler.class)
//    @ConditionalOnProperty(value = AutoloadCacheProperties.PREFIX + ".enable-read-and-write", matchIfMissing = true)
    public CacheMethodInterceptor autoloadCacheMethodInterceptor(CacheHandler cacheHandler) {
        return new CacheMethodInterceptor(cacheHandler, config);
    }

    @Bean
    public DefaultPointcutAdvisor defaultPointcutAdvisor(CacheMethodInterceptor methodInterceptor) {
        DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor();
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression(traceExecution);
        advisor.setPointcut(pointcut);
        advisor.setAdvice(methodInterceptor);
        return advisor;
    }

}
